% splitidx are not supported, currently
\CUSDependency{disable={splitidx,imakeidx,makeindex}}
\CUSProvideExplLibrary{index}{\cus@d@te}{\cus@versi@n}{index and glossary}

\msg_new:nnn { cus } { index-exist } { Index~`#1'~already~eixsts. }
\msg_new:nnn { cus } { index-unknown } { Index~`#1'~is~unknown. }
\msg_new:nnn { cus } { index-key-unknown } { Setup~key~`#2'~of~index~`#1'~is~unknown. }

\cs_set_protected_nopar:Npn \indexspace { \par \vskip 10\p@ \@plus 5\p@ \@minus 5\p@ \relax }

\clist_const:Nn \c__cus_index_key_clist
  {
    filename , % index filename 
    output , % output filename 
    name , % index id 
    title , % plain text 
    program , % program 
    options , % options 
    exec , % shell 
    auto , % auto call program 
    multi , % multicolumn options 
    heading , % heading level 
    heading* , % heading cmd 
    init , %
    prologue , %
  }
\prop_const_from_keyval:Nn \c__cus_index_prop
  {
    % filename = \jobname.idx , %
    % output = \jobname.ind , %
    name     = , % index id 
    title    = \indexname , % plain text 
    program  = makeindex , % program 
    options  = , % options 
    % exec     = makeindex~\jobname @.idx , % shell 
    auto     = 0 , % auto call program
    multi    = 2 , % multicolumn options 
    heading  = 0, % heading level 
    heading* = \chapter* , % heading cmd 
    init     = , %
    prologue = , %
  }
\seq_new:N \g__cus_index_seq

\prg_new_conditional:Npnn \cus_index_if_exist:n #1 { p, T, F, TF }
  {
    \cs_if_exist:cTF { @indexfile #1 }
      { \prg_return_true: } { \prg_return_false: }
  }

\NewDocumentCommand \setupindex { >{\TrimSpaces} O{} }
  {
    \keyval_parse:nnn { \msg_warning:nnn { cus } { key-missing-value } }
      { \__cus_index_setup:nnn {#1} }
  }
% 使用次数很少，一点点性能浪费，问题不大
\cs_new:Npn \__cus_index_setup:nnn #1#2#3
  {
    \tl_if_empty:nTF {#1}
      { \prop_gput:cnn { g__cus_index-#1_prop } {#2} {#3} }
      {
        \str_if_eq:eeTF { #1 } { * }
          {
            \seq_map_inline:Nn \g__cus_index_seq
              { \prop_gput:cnn { g__cus_index-##1_prop } {#2} {#3} }
          }
          {
            \clist_map_inline:nn {#1}
              {
                \clist_if_in:NnTF \c__cus_index_key_clist {#2}
                  { \prop_gput:cnn { g__cus_index-##1_prop } {#2} {#3} }
                  { \msg_warning:nnxx { cus } { index-key-unknown } {##1} {#2} }
              }
          }
      }
  }

\NewDocumentCommand \newindextype { +O{} >{\TrimSpaces} m }
  {
    \cus_index_if_exist:nTF { #2 } 
      { \msg_warning:nnx { cus } { index-exist } {#2} }
      {
        \seq_gput_right:Nx \g__cus_index_seq {#2}
        \iow_new:c { @indexfile #2 }
        \tl_gset:cn { @index@basic~#2@init } { \g__cus_index_basic_init_tl }
        \cs_gset_eq:cN { g__cus_index-#2_prop } \c__cus_index_prop
        \exp_args:Ncx \prop_gput_from_keyval:Nn { g__cus_index-#2_prop } 
          { \exp_not:n {#1} , name=#2 }
        \prop_get:cnN { g__cus_index-#2_prop } { filename } \l__cus_filename_tl 
        \quark_if_no_value:NTF \l__cus_filename_tl
          { 
            \tl_set:Nx \l__cus_filename_tl { \c_sys_jobname_str @ #2 .idx } 
            \prop_gput:cno { g__cus_index-#2_prop } { filename } \l__cus_filename_tl
          }
          { \prop_gput:cnx { g__cus_index-#2_prop } { filename } \l__cus_filename_tl }
        \iow_open:cn { @indexfile #2 } { \l__cus_filename_tl }
        \prop_get:cnN { g__cus_index-#2_prop } { output } \l__cus_output_tl
        \quark_if_no_value:NTF \l__cus_output_tl
          {
            \file_parse_full_name:nNNN { ./ \l__cus_filename_tl } \l__cus_file_dir_tl 
              \l__cus_file_name_tl \l__cus_file_ext_tl 
            \prop_put:cnx { g__cus_index-#2_prop } { output }
              { \l__cus_file_dir_tl / \l__cus_file_name_tl . ind }
          }
          { \prop_put:cnx { g__cus_index-#2_prop } { output } \l__cus_output_tl }
        \use:e 
          {
            \exp_not:N \cus_hook_gput_aa:n 
              {
                \iow_close:N \exp_not:c { @indexfile #2 }
                \tl_set:Nx \exp_not:N \l__cus_shell_tl
                  { \exp_not:N \prop_item:Nn \exp_not:c { g__cus_index-#2_prop } { exec } }
                \exp_not:N \tl_if_blank:oT \exp_not:N \l__cus_shell_tl
                  {
                    \prop_get:NnN \exp_not:c { g__cus_index-#2_prop } { program } \exp_not:N \l__cus_tmpb_tl
                    \prop_get:NnN \exp_not:c { g__cus_index-#2_prop } { options } \exp_not:N \l__cus_tmpc_tl
                    \tl_set:Nx \exp_not:N \l__cus_shell_tl 
                      { 
                        \exp_not:n { \l__cus_tmpb_tl \c_space_tl \l__cus_tmpc_tl \c_space_tl }
                        \quark_if_no_value:NF \l__cus_output_tl
                          { -o \c_space_tl \l__cus_output_tl \c_space_tl }
                        \l__cus_filename_tl
                      }
                  }
                \exp_not:N \bool_if:nT 
                  { 
                    \exp_not:N \str_case_e:nnF 
                      { \exp_not:N \prop_item:Nn \exp_not:c { g__cus_index-#2_prop } { auto } }
                      {
                        { 0 } { \c_false_bool }
                        { 1 } { \c_true_bool }
                        { true } { \c_true_bool }
                        { false } { \c_false_bool }
                        { } { \c_true_bool }
                      }
                      { \exp_not:N \prop_item:Nn \exp_not:c { g__cus_index-#2_prop } { auto } }
                  }
                  { \sys_shell_now:x \exp_not:N \l__cus_shell_tl }
              }
          }
      }
  }
\tl_new:N \g__cus_index_basic_init_tl
\NewDocumentCommand \setindexprologue { >{\TrimSpaces} O{} }
  { \tl_gset:cn { @index@#1@prologue } }
\NewDocumentCommand \setindexinit { >{\TrimSpaces} O{} }
  { \tl_gset:cn { @index@#1@init } }
\cs_new_protected_nopar:Npn \index@presetting #1 
  { 
    \cs:w @index@basic~#1@init \cs_end:
    \cs:w l__cus_index_init_tl \cs_end:
    \cs:w l__cus_index_prologue_tl \cs_end:
  }
\cs_new_protected:Npn \cus@theindex #1
  {
    \legacy_if:nTF { @twocolumn } 
      { \legacy_if_set_true:n { @restonecol } \onecolumn }
      { \legacy_if_set_false:n { @restonecol } }
    \cs_set_eq:NN \item \@idxitem
    \startmulticolumns [#1]
    \parindent \z@
    \parskip\z@ \@plus .3\p@\relax
  }
\cs_new_protected:Npn \endcus@theindex
  { 
    \stopmulticolumns
    \cs_gset_eq:NN \l__cus_index_empty_do_tl \scan_stop:
    \legacy_if:nTF { @restonecol } { \twocolumn } {  } 
  }
\cus_hook_gput_pe:n 
  {
    \DeclareDocumentCommand \makeindex { +O{} >{\TrimSpaces} O{} } 
      { \newindextype [{#1}] {#2} }
    \DeclareDocumentCommand \index { >{\TrimSpaces} O{} +m }
      {
        \cus_index_if_exist:nTF { #1 }
          {
            \@bsphack
            \group_begin:
            \cs_set_eq:Nc \@indexfile { @indexfile #1 }
            \@sanitize
            \@wrindex {#2}
          }
          { \msg_error:nnn { cus } { index-unknown } {#1} }
      }
    \DeclareDocumentCommand \printindex { +O{} }
      {
        \group_begin:
        \__cus_if_keyval:nTF {#1}
          { 
            \prop_set_from_keyval:Nn \l__cus_tmpa_prop {#1} 
            \tl_set:Nx \l__cus_index_name_tl { \prop_item:Nn \l__cus_tmpa_prop { name } }
            \cus_index_if_exist:nF { \l__cus_index_name_tl } 
              { \msg_error:nnx { cus } { index-unknown } { \l__cus_index_name_tl } }
            \exp_args:Ncc \prop_concat:NNN 
              { g__cus_index-\l__cus_index_name_tl _prop }
              { g__cus_index-\l__cus_index_name_tl _prop }
              \l__cus_tmpa_prop
          }
          { \tl_set:Nx \l__cus_index_name_tl { \tl_trim_spaces:n {#1} } }
        \prop_map_inline:cn { g__cus_index-\l__cus_index_name_tl _prop }
          { \tl_set:cn { l__cus_index_ ##1 _tl } {##2} }
        \tl_set:Nx \l__cus_index_heading_multi_tl
          {
            \tl_if_empty:NTF \l__cus_index_title_tl
              { }
              {
                \tl_if_empty:cTF { l__cus_index_heading*_tl }
                  {
                    \exp_not:o \l__cus_index_heading_tl
                    { \exp_not:o \l__cus_index_title_tl }
                  }
                  {
                    \exp_not:v { l__cus_index_heading*_tl }
                    { \exp_not:o \l__cus_index_title_tl }
                  }
              }
          }
        \tl_set:Nx \l__cus_index_empty_do_tl 
          { 
            \exp_not:o \l__cus_index_heading_multi_tl
            \exp_not:c { @index@#1@prologue }
          }
        \cs_set:Npx \theindex 
          { 
            \cus@theindex 
              { 
                heading= 
                  { 
                    \exp_not:o \l__cus_index_heading_multi_tl 
                    \exp_not:c { @index@#1@prologue }
                  } , 
                \exp_not:o \l__cus_index_multi_tl 
              } 
            \index@presetting { \l__cus_index_name_tl } 
            \par 
          }
        \cs_set_eq:NN \endtheindex \endcus@theindex 
        \cs:w @index@#1@init \cs_end:
        \@input@ { \l__cus_index_output_tl }
        \l__cus_index_empty_do_tl
        \group_end:
      }
  }